﻿namespace IBTrader.Frontend.HistoricalData
{
    using IBTrader.Model;
    using IBTrader.Modules.Configuration;
    using IBTrader.Modules.HistoricalData;
    using IBTrader.Modules.Tws;
    using IBTrader.Properties;
    using Microsoft.Practices.Prism.Commands;
    using MvvmValidation;
    using NLog;
    using System;
    using System.ComponentModel;
    using System.Linq;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Threading;

    class ViewModel : ViewModelBase
    {
        #region private fields
        private static readonly Logger logger = LogManager.GetCurrentClassLogger();
        private HistoricalData historicalData;
        private readonly DispatcherTimer timer = new DispatcherTimer();
        private event EventHandler PacingViolation;
        #endregion
        #region delegate commands
        public DelegateCommand Start { get; private set; }
        public DelegateCommand StartDownload { get; private set; }
        public DelegateCommand CancelDownload { get; private set; }
        public DelegateCommand<InitializingNewItemEventArgs> DataGridInitializingNewItem { get; private set; }
        public DelegateCommand<DataGrid> DataGridCurrentCellChanged { get; private set; }
        #endregion
        public ViewModel() : base(ComponentsSection.Components.HistoricalData)
        {
            if ((bool)DependencyPropertyDescriptor.FromProperty(DesignerProperties.IsInDesignModeProperty, typeof(DependencyObject)).Metadata.DefaultValue) return;
            
            #region Timer
            timer.Interval = new TimeSpan(0, 1, 0);
            PacingViolation += (__, _) =>
            {
                LogInfo(logger, "Timer", "Wait {0}", timer.Interval);
                timer.Start();
            };
            Action<object, EventArgs> OnTick = (_,__) =>
            {
                timer.Stop();
                if (historicalData != null && !historicalData.Error.Paused)
                    historicalData.Data.Request();
            };
            timer.Tick += new EventHandler(OnTick);
            #endregion
            #region Commands
            Start = new DelegateCommand(OnStart);
            StartDownload = new DelegateCommand(OnStartDownload, () => historicalData == null);
            CancelDownload = new DelegateCommand(() => { historicalData.Cancel(); OnTick(this, EventArgs.Empty); }, () => historicalData != null);
            DataGridInitializingNewItem = new DelegateCommand<InitializingNewItemEventArgs>(e =>
            {
                var item = e.NewItem as HistoricalContract;
                item.From = DateTime.Today.AddMonths(-6);
                item.To = DateTime.Today;
                item.BarDuration = CalcBarDuration("1 secs");
            });
            DataGridCurrentCellChanged = new DelegateCommand<DataGrid>(grid =>
            {
                var item = grid.SelectedItem as HistoricalContract;
                if (item == null || string.IsNullOrWhiteSpace(item.BarSize)) return;
                item.BarDuration = CalcBarDuration(item.BarSize);
            });
            #endregion

            Validator.AddRule(() => RuleResult.Assert(Store.HistoricalContracts.Any(), "At least one contract must be configured"));
        }
        protected override void OnLoad(System.Windows.Controls.Control view)
        {
            if (IsLoaded) return;
            if (IB == null) return;

            IB.errMsg += (_, e) => { if (historicalData != null) historicalData.Error.Handle(e); };
            IB.historicalData += (_, i) =>
            {
                if (historicalData != null)
                    historicalData.File.Write(i);
                else
                    LogInfo(logger, "historicalData", "{0} {1} {2} {3} {4}", i.date, i.open, i.high, i.low, i.close);
            };
            IB.historicalDataEnd += (_, __) =>
            {
                if (historicalData != null)
                    historicalData.Next();
                else
                    LogInfo(logger, "historicalDataEnd");
            };
            base.OnLoad(view);
        }
        protected override void OnConnect()
        {
            if (IsValid())
                base.OnConnect();
        }
        private void OnStart()
        {
            if (IB.IsConnected())
                OnDisconnect();
            if (!IB.IsConnected())
                OnConnect();
            if (IB.IsConnected())
                OnStartDownload();
        }
        private void OnStartDownload()
        {
            foreach (var contract in Store.HistoricalContracts) contract.Percent = -1;
            Download();
        }
        private void Download()
        {
            var contract = Store.HistoricalContracts.Where(c => c.Percent < 0).FirstOrDefault();
            if (contract == null) return;

            historicalData = new HistoricalData(IB, contract);
            historicalData.Data.Next += () => LogInfo(logger, "HistoricalData", "{0} {1}", historicalData.Contract.Symbol, historicalData.Contract.DateTime);
            historicalData.Error.PacingViolation += PacingViolation;
            historicalData.Contract.End += () =>
            {
                LogInfo(logger, "HistoricalData", "End");
                historicalData = null;
                Download();
                Update(StartDownload, CancelDownload);
            };
            historicalData.Next();
            Update(StartDownload, CancelDownload);
        }
        private static TimeSpan CalcBarDuration(string barSize)
        {
            var parts = barSize.Split(' ');
            var duration = long.Parse(parts[0]);
            switch (parts[1])
            {
                case "sec":
                case "secs":
                    duration *= TimeSpan.TicksPerMinute * 30;
                    break;
                case "min":
                case "mins":
                    duration *= TimeSpan.TicksPerDay;
                    break;
                case "hour":
                    duration *= TimeSpan.TicksPerDay * 30;
                    break;
                default:
                    duration *= TimeSpan.TicksPerDay * 356;
                    break;
            }
            return new TimeSpan(duration);
        }
        public override bool AutoConnect
        {
            get { return Settings.Default.HistoricalDataAutoConnect; }
            set
            {
                Settings.Default.HistoricalDataAutoConnect = value;
                Settings.Default.Save();
            }
        }
    }
}